package cn.w.outdoor.lib.media;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.util.Log;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;

import cn.w.outdoor.lib.common.CommonMethods;
import cn.w.outdoor.lib.common.CommonVideoCodecMethods;
import cn.w.outdoor.lib.model.Trans_Package;
import cn.w.outdoor.lib.network.UDPTools;

public class H264VideoEncodeAndSendThread extends Thread {

    private OnDataListener dataListener = null;

    public void setDataListener(OnDataListener dataListener) {
        this.dataListener = dataListener;
    }

    private final static String TAG = "VideoEncodeAndSend";

    private Queue<byte[]> originalVideoBuffer; // 摄像头原始数据队列

    /**
     * 添加视频的原始数据内容
     */
    public void addOriginVideoData(byte[] data) {
        if (originalVideoBuffer == null) originalVideoBuffer = new ConcurrentLinkedQueue<>();
//        Log.e(TAG, "addOriginVideoData: 收到数据信息，数据长度：" + data.length);
        originalVideoBuffer.offer(data);
    }

    private MediaCodec encoder;
    private MediaFormat format;
    private int colorFormat;

    final int TIMEOUT_USEC = 10000;

    private boolean stopEncoder;

    public void stopEncoder() {
        this.stopEncoder = true;
    }

    public H264VideoEncodeAndSendThread() {
    }

    @Override
    public void run() {

        this.stopEncoder = false;

        MediaCodecInfo codecInfo = CommonVideoCodecMethods.selectCodec(MediaConfig.VIDEO_MIME_TYPE);
        if (codecInfo == null) {
            Log.e(TAG, "Unable to find an appropriate codec for " + MediaConfig.VIDEO_MIME_TYPE);
            return;
        } else {
            Log.e(TAG, "found codec: " + codecInfo.getName());
        }

        colorFormat = CommonVideoCodecMethods.selectColorFormat(codecInfo, MediaConfig.VIDEO_MIME_TYPE);
        if (colorFormat == -1) {
            Log.e(TAG, "执行 selectColorFormat 失败，启动解码显示线程失败");
            return;
        }

        format = MediaFormat.createVideoFormat(MediaConfig.VIDEO_MIME_TYPE, MediaConfig.VIDEOWIDTH, MediaConfig.VIDEOHEIGHT);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
        format.setInteger(MediaFormat.KEY_BIT_RATE, MediaConfig.VIDEO_BITRATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, MediaConfig.VIDEO_FRAMERATE);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, MediaConfig.VIDEO_IFRAMEINTERVAL);

        try {
            encoder = MediaCodec.createByCodecName(codecInfo.getName());
        } catch (IOException e) {
            Log.e(TAG, "创建解码器失败");
            e.printStackTrace();
            return;
        }

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        encoder.start();

        ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
        ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();

        Trans_Package.resetVideoPackageId();
        try {
            while (!stopEncoder) {

                byte[] originalData = originalVideoBuffer.poll();
                if (originalData == null) {
                    try {
                        Thread.sleep(10l);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    /**
                     * 放入解码前的数据
                     */
                    int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
//                    Log.e(TAG, "inputBufIndex=" + inputBufIndex);
                    if (inputBufIndex >= 0) {
                        ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
                        // the buffer should be sized to hold one full frame
                        inputBuf.clear();
                        inputBuf.put(originalData);
                        //                            encoder.queueInputBuffer(inputBufIndex, 0, originalData.length, ptsUsec, 0);
                        encoder.queueInputBuffer(inputBufIndex, 0, originalData.length, 0, 0);
//                        Log.e(TAG, "submitted frame " + generateIndex + " to enc");
                        //                        }
                    }

                    /**
                     * 获取解码后的数据
                     */
                    int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
                    if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        // no output available yet
                        Log.e(TAG, "no output from encoder available");
                    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        // not expected for an encoder
                        encoderOutputBuffers = encoder.getOutputBuffers();
                        Log.e(TAG, "encoder output buffers changed");
                    } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        // not expected for an encoder
                        MediaFormat newFormat = encoder.getOutputFormat();
                        Log.e(TAG, "encoder output format changed: " + newFormat);
                    } else if (encoderStatus < 0) {
                        Log.e(TAG, "unexpected r from encoder.dequeueOutputBuffer: " + encoderStatus);
                    } else { // encoderStatus >= 0
                        ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
                        if (encodedData == null) {
                            Log.e(TAG, "encoderOutputBuffer " + encoderStatus + " was null");
                        }
                        // It's usually necessary to adjust the ByteBuffer values to match BufferInfo.
                        encodedData.position(info.offset);
                        encodedData.limit(info.offset + info.size);
                        byte[] encodedVideoData = new byte[info.size];
                        encodedData.get(encodedVideoData);
                        encodedData.position(info.offset);

//

                        if (dataListener != null) {
//                            Trans_Package trans_package = new Trans_Package(Trans_Package.vType_Video, encodedVideoData);
//                            Log.e(TAG, "获取到编码数据 长度:" + encodedVideoData.length);
//                            Log.e(TAG, "打包后的数据长度：" + trans_package.getLen());
//                            Log.e(TAG, "开头部分数据内容：" + CommonMethods.byte2hex(trans_package.generateToBytes(), 30));
                            dataListener.onData(new Trans_Package(Trans_Package.vType_Video, encodedVideoData));
                        }
                        encoder.releaseOutputBuffer(encoderStatus, false);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (encoder != null) {
                try {
                    encoder.stop();
                    encoder.release();
                    encoder = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
